#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#include <sys/shm.h>

#include <stdio.h>
#include <fcntl.h>


#define BUF_SIZE 1024

union semun
{
	int val;                /* value for SETVAL */
	struct semid_ds *buf;   /* buffer for IPC_STAT & IPC_SET */
	ushort *array;          /* array for GETALL & SETALL */
	struct seminfo *__buf;  /* buffer for IPC_INFO */
	void *__pad;
};

int main( int argc, char **argv)
{
	if ( argc != 1)
	{
		printf("Wrong arguments\n");
		exit(0);
	}
	
	int fd =  open( ".for_keys", O_CREAT | 0644);
	if ( fd != -1)
		close(fd);
///////////////////////////////////////////////
		struct sembuf sem_undo_inc, sem_undo_dec;
		
		sem_undo_inc.sem_num = 0;
		sem_undo_inc.sem_flg = SEM_UNDO;
		sem_undo_inc.sem_op  = 1;
		
		sem_undo_dec.sem_num = 0;
		sem_undo_dec.sem_flg = SEM_UNDO;
		sem_undo_dec.sem_op  = -1;
		
		struct sembuf sem_inc2, sem_dec2;
		
		sem_inc2.sem_num = 0;
		sem_inc2.sem_flg = NULL;
		sem_inc2.sem_op  = 2;
		
		sem_dec2.sem_num = 0;
		sem_dec2.sem_flg = NULL;
		sem_dec2.sem_op  = -2;
		
		struct sembuf sem_inc, sem_dec;
		
		sem_inc.sem_num = 0;
		sem_inc.sem_flg = NULL;
		sem_inc.sem_op  = 1;
		
		sem_dec.sem_num = 0;
		sem_dec.sem_flg = NULL;
		sem_dec.sem_op  = -1;
///////////////////////////////////////////////
	key_t key_sem_common_empty  = ftok( ".for_keys", 1);
	key_t key_sem_common_full   = ftok( ".for_keys", 2);
	key_t key_sem_common_mutex  = ftok( ".for_keys", 3);
	
	key_t key_shm_common = ftok( ".for_keys", 10);
///////////
	int sem_id_common_mutex = semget( key_sem_common_mutex,1,IPC_CREAT | IPC_EXCL | 0644);
	if ( sem_id_common_mutex == -1){
		sem_id_common_mutex = semget( key_sem_common_mutex ,1, NULL);
	}
	else
	{
		semop( sem_id_common_mutex, &sem_inc2, 1);
	}
	
	int sem_id_common_empty  = semget( key_sem_common_empty ,1,IPC_CREAT | IPC_EXCL | 0644);
	if ( sem_id_common_empty == -1){
		sem_id_common_empty  = semget( key_sem_common_empty ,1, NULL);
	}
	else
	{
		semop( sem_id_common_empty, &sem_inc2, 1);
	}
	
	int sem_id_common_full = semget( key_sem_common_full,1,IPC_CREAT | IPC_EXCL | 0644);
	if ( sem_id_common_full == -1){
		sem_id_common_full = semget( key_sem_common_full,1, 0644);
	}
	///////////////
	int shm_id_common = shmget( key_shm_common, 4*sizeof( int), IPC_CREAT | IPC_EXCL | 0644);
	if ( shm_id_common == -1){
		shm_id_common = shmget( key_shm_common, 4*sizeof( int), 0644);
	}	
	///////////////
	int  * ptr_shm_common = (int* ) shmat( shm_id_common, NULL, NULL);
////////////////////////////////////////////////
	semop ( sem_id_common_full, &sem_dec2,1);
		semop ( sem_id_common_mutex, &sem_dec2,1);

			int shm_id_buffer       = ptr_shm_common[ 0];
			int shm_id_count        = ptr_shm_common[ 1];
			int sem_id_buffer_read  = ptr_shm_common[ 2];
			int sem_id_buffer_write = ptr_shm_common[ 3];
		
		semop ( sem_id_common_mutex ,&sem_inc2,1);
	semop ( sem_id_common_empty, &sem_inc2,1);
	////////////////////////////////////////////////
	printf("sem_read %d \t sem_write %d\n",sem_id_buffer_read,sem_id_buffer_write);
	
	char * ptr_shm_buffer = (char*) shmat( shm_id_buffer, NULL, SHM_RDONLY);
	int  * ptr_shm_count  = (int* ) shmat( shm_id_count , NULL, SHM_RDONLY);
	////////////////////////////////////////////////
	int fd_out = 1; //stdout
	
	do
	{
		semop( sem_id_buffer_write, &sem_inc,1);
		
		semop( sem_id_buffer_read, &sem_dec,1);
			int write_count = 0;
			do
			{
				int tmp = write( fd_out, ptr_shm_buffer, *ptr_shm_count);
				if ( tmp == -1)
				{
					perror("Out write error");
				}
				write_count +=tmp;
			}
			while ( write_count != *ptr_shm_count);
	}
	while( *ptr_shm_count != 0);

	semctl( sem_id_buffer_read, 0, IPC_RMID, 0);
	semctl( sem_id_buffer_write, 0, IPC_RMID, 0);
	
	shmdt( ptr_shm_common);
	shmdt( ptr_shm_buffer);
	shmdt( ptr_shm_count);
	
//	semop( sem_id_common_mutex, &sem_inc, 1);
//	semop( sem_id_common_empty, &sem_inc, 1);
}
